﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace Pub
{
    /// <summary>
    /// TEA 加密解密
    /// </summary>
    public class CTEA
    {

        private static void code(byte[] In, int inOffset, int inPos, byte[] Out, int outOffset, int outPos, byte[] key)
        {
            if (outPos > 0)
            {
                for (int i = 0; i < 8; i++)
                {
                    In[outOffset + outPos + i] = (byte)(In[inOffset + inPos + i] ^ Out[outOffset + outPos + i - 8]);
                }
            }
            uint[] formattedKey = FormatKey(key);
            uint y = ConvertByteArrayToUInt(In, outOffset + outPos);
            uint z = ConvertByteArrayToUInt(In, outOffset + outPos + 4);
            uint sum = 0;
            uint delta = 0x9e3779b9;
            uint n = 16;

            while (n-- > 0)
            {
                sum += delta;
                y += ((z << 4) + formattedKey[0]) ^ (z + sum) ^ ((z >> 5) + formattedKey[1]);
                z += ((y << 4) + formattedKey[2]) ^ (y + sum) ^ ((y >> 5) + formattedKey[3]);
            }
            Array.Copy(ConvertUIntToByteArray(y), 0, Out, outOffset + outPos, 4);
            Array.Copy(ConvertUIntToByteArray(z), 0, Out, outOffset + outPos + 4, 4);
            if (inPos > 0)
            {
                for (int i = 0; i < 8; i++)
                {
                    Out[outOffset + outPos + i] = (byte)(Out[outOffset + outPos + i] ^ In[inOffset + inPos + i - 8]);
                }
            }
        }

        private static void decode(byte[] In, int inOffset, int inPos, byte[] Out, int outOffset, int outPos, byte[] key)
        {
            if (outPos > 0)
            {
                for (int i = 0; i < 8; i++)
                {
                    Out[outOffset + outPos + i] = (byte)(In[inOffset + inPos + i] ^ Out[outOffset + outPos + i - 8]);
                }
            }
            else
            {
                Array.Copy(In, inOffset, Out, outOffset, 8);
            }
            uint[] formattedKey = FormatKey(key);
            uint y = ConvertByteArrayToUInt(Out, outOffset + outPos);
            uint z = ConvertByteArrayToUInt(Out, outOffset + outPos + 4);
            uint sum = 0xE3779B90;
            uint delta = 0x9e3779b9;
            uint n = 16;

            while (n-- > 0)
            {
                z -= ((y << 4) + formattedKey[2]) ^ (y + sum) ^ ((y >> 5) + formattedKey[3]);
                y -= ((z << 4) + formattedKey[0]) ^ (z + sum) ^ ((z >> 5) + formattedKey[1]);
                sum -= delta;
            }
            Array.Copy(ConvertUIntToByteArray(y), 0, Out, outOffset + outPos, 4);
            Array.Copy(ConvertUIntToByteArray(z), 0, Out, outOffset + outPos + 4, 4);
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="In">密文</param>
        /// <param name="offset">密文开始的位置</param>
        /// <param name="len">密文长度</param>
        /// <param name="key">密钥</param>
        /// <returns>返回明文</returns>
        public static byte[] Decrypt(byte[] In, int offset, int len, byte[] key)
        {
            // 因为QQ消息加密之后至少是16字节，并且肯定是8的倍数，这里检查这种情况
            if ((len % 8 != 0) || (len < 16))
            {
                return null;
            }
            byte[] Out = new byte[len];
            for (int i = 0; i < len; i += 8)
            {
                decode(In, offset, i, Out, 0, i, key);
            }
            for (int i = 8; i < len; i++)
            {
                Out[i] = (byte)(Out[i] ^ In[offset + i - 8]);
            }
            int pos = Out[0] & 0x07;
            len = len - pos - 10;
            byte[] res = new byte[len];
            Array.Copy(Out, pos + 3, res, 0, len);
            return res;
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="sIn">16进制字符串</param>
        /// <param name="sKey">16进制字符串key</param>
        /// <returns>16进制字符串</returns>
        public static string Decrypt(string sIn, string sKey)
        {
            string sTxt = sIn;
            byte[] b_Dat = CPub.str_to_bytes(sTxt);
            byte[] b_Key = CPub.str_to_bytes(sKey);
            int iLen = b_Dat.Length;

            byte[] b_DatII = CTEA.Decrypt(b_Dat, 0, iLen, b_Key);
            string sTxtII = CPub.bytes_to_str(b_DatII);
            return sTxtII;
        }

        /// <summary>
        /// 16进制加密字符串 使用普通字符串作为key 进行解密
        /// </summary>
        /// <param name="sIn">16进制字符串 可以包含空格</param>
        /// <param name="sKey">普通字符串key</param>
        /// <returns>解密后的字符串</returns>
        public static string TEADecryptStr(string sIn, string sKey)
        {
            byte[] b_Dat = CPub.str_to_bytes(sIn);
            byte[] b_Key = System.Text.Encoding.Default.GetBytes(sKey);
            int iLen = b_Dat.Length;
            byte[] b_DatII = CTEA.Decrypt(b_Dat, 0, iLen, b_Key);
            string s2 = System.Text.Encoding.Default.GetString(b_DatII);
            return s2;
        }

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="In">密文</param>
        /// <param name="offset">密文开始位置</param>
        /// <param name="len">密文长度</param>
        /// <param name="key">密钥</param>
        /// <returns></returns>
        public static byte[] Encrypt(byte[] In, int offset, int len, byte[] key)
        {
            // 计算头部填充字节数
            int pos = (len + 10) % 8;
            if (pos != 0)
            {
                pos = 8 - pos;
            }
            Random Rnd = new Random();
            byte[] plain = new byte[len + pos + 10];
            plain[0] = (byte)((Rnd.Next(1,100) & 0xF8) | pos);
            for (int i = 1; i < pos + 3; i++)
            {
                plain[i] = (byte)(Rnd.Next(1,100) & 0xFF);
            }
            Array.Copy(In, 0, plain, pos + 3, len);
            for (int i = pos + 3 + len; i < plain.Length; i++)
            {
                plain[i] = 0x0;
            }
            // 定义输出流
            byte[] outer = new byte[len + pos + 10];
            for (int i = 0; i < outer.Length; i += 8)
            {
                code(plain, 0, i, outer, 0, i, key);
            }
            return outer;
        }

        /// <summary>
        /// 16进制字符串 使用16进制key 进行加密
        /// </summary>
        /// <param name="sIn">16进制字符串</param>
        /// <param name="sKey">16进制字符串key</param>
        /// <returns>16进制字符串</returns>
        public static string Encrypt(string sIn, string sKey)
        {
            string sTxt = sIn;
            byte[] b_Dat = CPub.str_to_bytes(sTxt);
            byte[] b_Key = CPub.str_to_bytes(sKey);
            int iLen = b_Dat.Length;

            byte[] b_DatII = CTEA.Encrypt(b_Dat, 0, iLen, b_Key);
            string sTxtII = CPub.bytes_to_str(b_DatII);
            return sTxtII;
        }

        /// <summary>
        /// 普通字符串 只用普通字符串作为key 加密
        /// </summary>
        /// <param name="sIn">原始字符串 普通字符串</param>
        /// <param name="sKey">key 普通字符串</param>
        /// <returns>16进制字符串</returns>
        public static string TEAEncryptStr(string sIn, string sKey)
        {
            //byte[] b1 = new byte[sIn.Length];
            byte[] b1 = System.Text.Encoding.Default.GetBytes(sIn);
            byte[] bKey = System.Text.Encoding.Default.GetBytes(sKey);
            byte[] b2 = Encrypt(b1, 0, b1.Length, bKey);
            string s2 = "";
            for (int i = 0; i < b2.Length;i++ )
            {
                s2 += b2[i].ToString("X2");
            }
            return s2;
        }

        private static uint[] FormatKey(byte[] key)
        {
            if (key.Length == 0)
            {
                throw new ArgumentException("Key must be between 1 and 16 characters in length");
            }
            byte[] refineKey = new byte[16];
            if (key.Length < 16)
            {
                Array.Copy(key, 0, refineKey, 0, key.Length);
                for (int k = key.Length; k < 16; k++)
                {
                    refineKey[k] = 0x20;
                }
            }
            else
            {
                Array.Copy(key, 0, refineKey, 0, 16);
            }
            uint[] formattedKey = new uint[4];
            int j = 0;
            for (int i = 0; i < refineKey.Length; i += 4)
            {
                formattedKey[j++] = ConvertByteArrayToUInt(refineKey, i);
            }
            return formattedKey;
        }

        private static byte[] ConvertUIntToByteArray(uint v)
        {
            byte[] result = new byte[4];
            result[0] = (byte)((v >> 24) & 0xFF);
            result[1] = (byte)((v >> 16) & 0xFF);
            result[2] = (byte)((v >> 8) & 0xFF);
            result[3] = (byte)((v >> 0) & 0xFF);
            return result;
        }

        private static uint ConvertByteArrayToUInt(byte[] v, int offset)
        {
            if (offset + 4 > v.Length)
            {
                return 0;
            }
            uint output;
            output = (uint)(v[offset] << 24);
            output |= (uint)(v[offset + 1] << 16);
            output |= (uint)(v[offset + 2] << 8);
            output |= (uint)(v[offset + 3] << 0);
            return output;
        }
    }

    /// <summary>
    /// AES 加密解密
    /// </summary>
    public class CAES
    {
        /// <summary>
        /// AES加密
        /// </summary>
        /// <param name="Data">被加密的明文</param>
        /// <param name="Key">密钥</param>
        /// <param name="Vector">向量</param>
        /// <returns>密文</returns>
        public static Byte[] AESEncrypt(Byte[] Data, String Key, String Vector)
        {
            Byte[] bKey = new Byte[32];
            Array.Copy(Encoding.UTF8.GetBytes(Key.PadRight(bKey.Length)), bKey, bKey.Length);
            Byte[] bVector = new Byte[16];
            Array.Copy(Encoding.UTF8.GetBytes(Vector.PadRight(bVector.Length)), bVector, bVector.Length);

            Byte[] Cryptograph = null; // 加密后的密文

            Rijndael Aes = Rijndael.Create();
            try
            {
                // 开辟一块内存流
                using (MemoryStream Memory = new MemoryStream())
                {
                    // 把内存流对象包装成加密流对象
                    using (CryptoStream Encryptor = new CryptoStream(Memory,
                     Aes.CreateEncryptor(bKey, bVector),
                     CryptoStreamMode.Write))
                    {
                        // 明文数据写入加密流
                        Encryptor.Write(Data, 0, Data.Length);
                        Encryptor.FlushFinalBlock();

                        Cryptograph = Memory.ToArray();
                    }
                }
            }
            catch
            {
                Cryptograph = null;
            }

            return Cryptograph;
        }

        public static String AESEncryptStr(string s1, string sKey)
        {
            string Vector = "";
            byte[] b1 = System.Text.Encoding.Default.GetBytes(s1);
            byte[] b2 = AESEncrypt(b1, sKey, Vector);
            string s2 = "";
            for (int i = 0; i < b2.Length; i++)
            {
                s2 += b2[i].ToString("X2");
            }
            return s2;
        }

        /// <summary>
        /// AES解密
        /// </summary>
        /// <param name="Data">被解密的密文</param>
        /// <param name="Key">密钥</param>
        /// <param name="Vector">向量</param>
        /// <returns>明文</returns>
        public static Byte[] AESDecrypt(Byte[] Data, String Key, String Vector)
        {
            Byte[] bKey = new Byte[32];
            Array.Copy(Encoding.UTF8.GetBytes(Key.PadRight(bKey.Length)), bKey, bKey.Length);
            Byte[] bVector = new Byte[16];
            Array.Copy(Encoding.UTF8.GetBytes(Vector.PadRight(bVector.Length)), bVector, bVector.Length);

            Byte[] original = null; // 解密后的明文

            Rijndael Aes = Rijndael.Create();
            try
            {
                // 开辟一块内存流，存储密文
                using (MemoryStream Memory = new MemoryStream(Data))
                {
                    // 把内存流对象包装成加密流对象
                    using (CryptoStream Decryptor = new CryptoStream(Memory,
                    Aes.CreateDecryptor(bKey, bVector),
                    CryptoStreamMode.Read))
                    {
                        // 明文存储区
                        using (MemoryStream originalMemory = new MemoryStream())
                        {
                            Byte[] Buffer = new Byte[1024];
                            Int32 readBytes = 0;
                            while ((readBytes = Decryptor.Read(Buffer, 0, Buffer.Length)) > 0)
                            {
                                originalMemory.Write(Buffer, 0, readBytes);
                            }

                            original = originalMemory.ToArray();
                        }
                    }
                }
            }
            catch
            {
                original = null;
            }

            return original;
        }

        public static string AESDecryptStr(string s1, string sKey)
        {
            string Vector = "";
            byte[] b1 = CPub.str_to_bytes(s1);
            byte[] b2 = AESDecrypt(b1, sKey, Vector);
            string s2 = System.Text.Encoding.Default.GetString(b2);
            return s2;
        }
    }

    public class CMD5
    {
        //原始方法 System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile("zzz");

        public static string GetMd5Str16(string ConvertString)
        {
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
            string t2 = BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(ConvertString)), 4, 8);
            t2 = t2.Replace("-", "");
            return t2;
        }

        public static string GetMd5Str(string ConvertString)
        {
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
            string t2 = BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(ConvertString)));
            t2 = t2.Replace("-", "");
            return t2;
        }

    }

    /// <summary>
    /// SHA1哈希算法实现
    /// </summary>
    public class SHA1 
    {
        //原始方法 System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile("zzz");

        public static string GetSHA1Str(string str_sha1_in)
        {
            SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
            byte[] bytes_sha1_in = UTF8Encoding.Default.GetBytes(str_sha1_in);
            byte[] bytes_sha1_out = sha1.ComputeHash(bytes_sha1_in);
            string str_sha1_out = BitConverter.ToString(bytes_sha1_out);
            str_sha1_out = str_sha1_out.Replace("-", "");
            return str_sha1_out;
        }
    }
}
